local _isWindows = os.getenv("OS") and os.getenv("OS"):find("Windows")
function isWindows()
	return _isWindows
end

local log_file = io.open([[log.txt]], "a+")

local simc_root_path, simc_path, base_profile_path
if isWindows() then
	simc_root_path = [[D:\SimC]]
	simc_path = [[D:\SimC\engine\simc]]
	base_profile_path = [[D:\SimC\profiles\Tier16H\]]
else
	simc_root_path = [[../../simc/]]
	simc_path = [[../../simc/engine/simc]]
	base_profile_path = [[../../simc/profiles/Tier16H/]]

	-- os.clock check for some lua binary on Linux OS: CentOS / RedHat etc.
	print("Detecting os.clock() bug ...")
	os.execute("sleep 1")
	if os.clock() == 0 then
		os.clock = os.time
		print("Replaced os.clock() as os.time()")
	end
end

local stats = setmetatable( {
	-- [489] = 1079,
	-- [497] = 1162,
	-- [502] = 1218,
	-- [510] = 1312,

	-- [522] = 1467,
	-- [526] = 1522,
	-- [528] = 1552,
	-- [530] = 1580,
	-- [532] = 1610,
	-- [535] = 1657,
	-- [536] = 1672,
	-- [539] = 1719,
	-- [541] = 1752,
	-- [543] = 1785,
	-- [545] = 1818,
	-- [549] = 1887,
	-- [551] = 1923,
}, { __index = function ( tbl, key )
		 -- y = 11.30404888 e9.321765499·10-3 x
		 return math.floor(11.30404888 * math.exp(9.321765499e-3 * key ))
	end,
} )

function find_item( list, key, value )
	for index, item in pairs(list) do
		if type(key) == "function" and key(item) then
			return item
		elseif value and item[key] == value then
			return item
		end
	end
end

function trinket_name( name, ilvl, suffix )
	return name .. "_" .. ilvl-- .. suffix
end

local token_cache = {}
function parse_token( itemName )
	if token_cache[itemName] then
		return token_cache[itemName]
	else
		local name = itemName:lower():gsub("%s", "_"):gsub("[^%a%_]", "")
		if name ~= itemName then
			log("Parsing trinket name %s => %s", itemName, name)
		end
		token_cache[itemName] = name
		return name
	end
end

function encode_java_str( str )
	-- loadfile([[F:\WoW-EN\Interface\addons\UTF8\utf8.lua]])()
	local code = ""
	for index = 1, #str do
	-- for index = 1, #string.utf8len(str) do
		-- local
		code = code .. ("\\u%02x"):format(string.byte(str:sub(index, index)))
		-- code = code .. ("\\u%02x"):format(string.byte(str:sub(index, index)))
	end
	print("Encode", str, code)
	return code
end

function random_name()
	local dict = "0123456789ABCDEF"
	local name = ""
	for index = 1, 8 do
		local index = math.random(1, #dict)
		name = name .. dict:sub(index, index)
	end
	return name
end

function find_default_vary( trinket )
	local vary = find_item( trinket.varies, "suffix", "N" )
	return vary or trinket.varies[1]
end

function get_line( slot, trinket )
	if not trinket then
		return ""
	end
	local line = ("trinket%d=%s,id=%d,upgrade=%d"):format(
		slot,
		trinket.token,
		trinket.id,
		trinket.upgrade
	)
	if trinket.constantStat then
		if session.bestStat and trinket.constantStat ~= session.bestStat then
			if trinket.reforgeAll then
				local stat = stats[trinket.ilvl]
				if not stat then
					log( "Unknown stat for trinket %s at ilvl %d", trinket.name, trinket.ilvl )
					return ""
				end
				line = line .. (",enchant=-%d%s_%d%s"):format(stat, trinket.constantStat, stat, session.bestStat)
			else
				line = line .. (",reforge=%s_%s"):format(trinket.constantStat, session.bestStat)
			end
		else
			log( "Best stat not specified for reforge %s", trinket.token )
		end
	end
	return line
end

function parse_trinket( trinket, index, upgrade )
	log( "parse_trinket %s #%d +%d", trinket.name, index, upgrade )
	local index = (index == "max") and #trinket.varies or index
	local vary = trinket.varies[index]
	local upgrade = upgrade

	if not vary then
		return nil, "Vary not exist"
	end
	if upgrade == "max" then
		upgrade = vary.maxupgrade or 4
	end
	if vary.maxupgrade and upgrade > vary.maxupgrade then
		return nil, "Upgrade exceeded item limit"
	end

	local ilvl = vary.ilvl + upgrade * 4
	if session.min_ilvl and session.min_ilvl > ilvl then
		return
	end
	local t = {}
	for key, value in pairs( trinket ) do
		t[key] = value
	end
	t.varies = nil
	t.name = trinket.name
	t.print_name = trinket_name(trinket.name, ilvl, vary.suffix)
	t.bbcode = ("[item=%d]%s[/item]"):format(find_default_vary(trinket).id, trinket.name, ilvl)
	t.ilvl = ilvl
	t.base_ilvl = find_default_vary(trinket).ilvl
	t.id = vary.id
	t.upgrade = upgrade
	t.token = parse_token(trinket.token)
	return t
end

function generate_profiles( trinkets )
	for _, trinket in ipairs( trinkets ) do
		generate_profiles_trinket(trinket)
		if trinket.constantStat == "exp" or trinket.constantStat == "hit" then
			-- trinket.name = trinket.name .. "Sat"
			trinket.reforgeAll = true
			generate_profiles_trinket(trinket)
		end
	end
end

function generate_profiles_trinket( trinket )
	for index = 1, #trinket.varies do
		for upgrade = 0, 4, 2 do
			local profile = parse_trinket( trinket, index, upgrade )
			if profile then
				profile.profile = get_line( 1, profile )
				table.insert(session.profiles, profile)
			end
		end
	end
end

function process_profile( profile, index )
	if profile.dps then
		if index then
			log(("Skipped profile %d / %d"):format(index, #session.profiles))
			return 0, 0
		end
	else
		local startTime = os.clock()
		if index then
			log(("Processing profile %d / %d ..."):format(index, #session.profiles))
		end

		-- Write temp simc profile
		local tmp_file = "Trinket_gen_tmp.simc"
		local profile_file = io.open(tmp_file, "w")
		local char_name = random_name()

		profile_file:write(base_profile_path, session.baseChar, ".simc", "\n\n")

		profile_file:write(session.header, "\n")

		profile_file:write("name=", char_name)
		profile_file:write("\n", "output=Trinket_gen_output.txt", "\n")
		profile_file:write("iterations=", session.iterations, "\n")
		profile_file:write(profile.profile, "\n")
		profile_file:close()

		-- The output of simc could be read by io.popen("simc.exe")
		-- However, it requires extra logic to handle progress bar
		local simcStartTime = os.clock()
		if isWindows() then
			os.execute(("%s %s"):format(simc_path, tmp_file))
			-- os.execute(("start /LOW /B /WAIT %s %s"):format(simc_path, tmp_file))
		else
			os.execute(("%s %s"):format(simc_path, tmp_file))
		end
		local simcElapsed = os.clock() - simcStartTime

		-- So we dump output into file and then process
		local source = io.open([[Trinket_gen_output.txt]], "r")
		local str = source:read("*all")
		source:close()
		local name, dps, err = str:match([[Player: (%S+) %S+ %S+ %S+ %S+
  DPS: ([%d%.]+)  DPS%-Error=([%d%.]+)]])

		-- Check if we get correct char name
		if name and tonumber(dps) > 0 then
			if name ~= char_name then
				log("Output char name mismatch: %s expected, got %s", char_name, char)
				return
			end
			log("Sim complete for %s: %.1f - %.1f", name, dps, err)
		else
			log("Failed fetching dps from report. Dumping output file")
			log("=====================")
			log("%s", str)
			log("=====================")
		end

		-- Potential error - item name inconsistency
		local customName, formalName, id = str:match([[inconsistency between name '(%S+)' and '(%S+)' for id (%d+)]])
		if customName then
			log("Name inconsistency: %s => %s #%d", formalName, customName, id)
		end

		profile.dps = tonumber(dps)
		profile.err = tonumber(err)
		save_session()
		return os.clock() - startTime, simcElapsed
	end
end

function write_table( file, table, indent )
	local indent = indent or ""
	for key, value in pairs(table) do
		file:write(indent)
		if type(key) == "number" then
			file:write("[", key, "]")
		elseif type(key) == "string" then
			file:write("[", string.format("%q", key), "]")
		end
		file:write(" = ")
		if type(value) == "number" then
			file:write(value)
		elseif type(value) == "string" then
			file:write(string.format("%q", value))
		elseif type(value) == "table" then
			file:write("{\n")
			write_table(file, value, indent .. "\t")
			file:write(indent)
			file:write("}")
		elseif type(value) == "boolean" then
			file:write(tostring(value))
		else
			file:write("nil")
		end
		file:write(",\n")
	end
end

function save_session(force_save)
	if (not force_save) and (prev_save_session and os.time() - prev_save_session < 10) then
		return
	end
	local file = io.open("Sessions/" .. session.name .. ".lua", "w")
	file:write("session = {\n")
	write_table( file, session, "\t" )
	file:write("}\n")
	file:close()
	prev_save_session = os.time()
end

function log( msg, ... )
	local msg = msg:format(...)
	print(os.date() .. " " .. msg)
	if log_file then
		log_file:write(os.date(), " ", msg, "\n")
		if not isWindows() then
			log_file:flush()
		end
	end
end

function get_simc_version()
	local stream = io.popen("svn info " .. simc_root_path)
	local info = stream:read("*all")
	return info:match("Revision: (%d+)"), info:match("Last Changed Date: ([^\n]+)")
end
